package com.seepine.auth.interceptor;


import cn.hutool.core.util.StrUtil;
import com.seepine.auth.annotation.Expose;
import com.seepine.auth.annotation.Login;
import com.seepine.auth.annotation.NotExpose;
import com.seepine.auth.entity.AuthProperties;
import com.seepine.auth.exception.AuthException;
import com.seepine.auth.util.AuthUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * @author seepine
 */
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    AuthProperties authProperties;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        if (hasAnnotation(handler, Login.class)) {
            return true;
        }
        String token = httpServletRequest.getHeader(authProperties.header());
        Object user = AuthUtil.getUserByToken(token);
        //没有'不暴露'并且'有暴露',不拦截直接放行
        if (!hasAnnotation(handler, NotExpose.class) && hasAnnotation(handler, Expose.class)) {
            return true;
        }
        //需要拦截但请求头没有token,拦截不放行
        else if (StrUtil.isBlank(token)) {
            httpServletResponse.setStatus(403);
            throw new AuthException("not find " + authProperties.header());
        }
        //有token但token获取不到用户
        else if (user == null) {
            httpServletResponse.setStatus(401);
            throw new AuthException("invalid " + authProperties.header());
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, @Nullable ModelAndView modelAndView) {
    }

    /**
     * 清理资源
     *
     * @param httpServletRequest  httpServletRequest
     * @param httpServletResponse httpServletResponse
     * @param o                   o
     * @param e                   e
     */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) {
        AuthUtil.clear();
    }

    /**
     * 方法上有注解或类上有注解
     *
     * @param handler         handler
     * @param annotationClass annotationClass
     * @return boolean
     */
    private boolean hasAnnotation(Object handler, Class<? extends Annotation> annotationClass) {
        try {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            return method.isAnnotationPresent(annotationClass) || method.getDeclaringClass().isAnnotationPresent(annotationClass);
        } catch (Exception ignored) {
            return false;
        }
    }
}
